home *** CD-ROM | disk | FTP | other *** search
/ Chip 2002 July / 07_02.iso / macos / files / Netscape-mac-full.bin / Netscape-mac-full / Netscape Full Installer / Installer Modules / browser.xpi / viewer / Components / nsHelperAppDlg.js < prev    next >
Text File  |  2002-05-12  |  25KB  |  659 lines

  1. /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
  2. /* ***** BEGIN LICENSE BLOCK *****
  3.  * Version: NPL 1.1/GPL 2.0/LGPL 2.1
  4.  *
  5.  * The contents of this file are subject to the Netscape Public License
  6.  * Version 1.1 (the "License"); you may not use this file except in
  7.  * compliance with the License. You may obtain a copy of the License at
  8.  * http://www.mozilla.org/NPL/
  9.  *
  10.  * Software distributed under the License is distributed on an "AS IS" basis,
  11.  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
  12.  * for the specific language governing rights and limitations under the
  13.  * License.
  14.  *
  15.  * The Original Code is the Mozilla browser.
  16.  *
  17.  * The Initial Developer of the Original Code is 
  18.  * Netscape Communications Corporation.
  19.  * Portions created by the Initial Developer are Copyright (C) 2001
  20.  * the Initial Developer. All Rights Reserved.
  21.  *
  22.  * Contributor(s):
  23.  *  Bill Law    <law@netscape.com>
  24.  *  Scott MacGregor <mscott@netscape.com>
  25.  *
  26.  *
  27.  * Alternatively, the contents of this file may be used under the terms of
  28.  * either the GNU General Public License Version 2 or later (the "GPL"), or
  29.  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
  30.  * in which case the provisions of the GPL or the LGPL are applicable instead
  31.  * of those above. If you wish to allow use of your version of this file only
  32.  * under the terms of either the GPL or the LGPL, and not to allow others to
  33.  * use your version of this file under the terms of the NPL, indicate your
  34.  * decision by deleting the provisions above and replace them with the notice
  35.  * and other provisions required by the GPL or the LGPL. If you do not delete
  36.  * the provisions above, a recipient may use your version of this file under
  37.  * the terms of any one of the NPL, the GPL or the LGPL.
  38.  *
  39.  * ***** END LICENSE BLOCK ***** */
  40.  
  41. /* This file implements the nsIHelperAppLauncherDialog interface.
  42.  *
  43.  * The implementation consists of a JavaScript "class" named nsHelperAppDialog,
  44.  * comprised of:
  45.  *   - a JS constructor function
  46.  *   - a prototype providing all the interface methods and implementation stuff
  47.  *
  48.  * In addition, this file implements an nsIModule object that registers the
  49.  * nsHelperAppDialog component.
  50.  */
  51.  
  52.  
  53. /* ctor
  54.  */
  55. function nsHelperAppDialog() {
  56.     // Initialize data properties.
  57.     this.mLauncher = null;
  58.     this.mContext  = null;
  59.     this.mSourcePath = null;
  60.     this.choseApp  = false;
  61.     this.chosenApp = null;
  62.     this.givenDefaultApp = false;
  63.     this.strings   = new Array;
  64.     this.elements  = new Array;
  65.     this.updateSelf = true;
  66. }
  67.  
  68. nsHelperAppDialog.prototype = {
  69.     // Turn this on to get debugging messages.
  70.     debug: false,
  71.  
  72.     nsIMIMEInfo  : Components.interfaces.nsIMIMEInfo,
  73.  
  74.     // Dump text (if debug is on).
  75.     dump: function( text ) {
  76.         if ( this.debug ) {
  77.             dump( text ); 
  78.         }
  79.     },
  80.  
  81.     // This "class" supports nsIHelperAppLauncherDialog, and nsISupports.
  82.     QueryInterface: function (iid) {
  83.         if (!iid.equals(Components.interfaces.nsIHelperAppLauncherDialog) &&
  84.             !iid.equals(Components.interfaces.nsISupports)) {
  85.             throw Components.results.NS_ERROR_NO_INTERFACE;
  86.         }
  87.         return this;
  88.     },
  89.  
  90.     // ---------- nsIHelperAppLauncherDialog methods ----------
  91.  
  92.     // show: Open XUL dialog using window watcher.  Since the dialog is not
  93.     //       modal, it needs to be a top level window and the way to open
  94.     //       one of those is via that route).
  95.     show: function(aLauncher, aContext)  {
  96.          this.mLauncher = aLauncher;
  97.          this.mContext  = aContext;
  98.          // Display the dialog using the Window Watcher interface.
  99.          var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  100.                     .getService( Components.interfaces.nsIWindowWatcher );
  101.          this.mDialog = ww.openWindow( null, // no parent
  102.                                        "chrome://global/content/nsHelperAppDlg.xul",
  103.                                        null,
  104.                                        "chrome,titlebar,dialog=yes",
  105.                                        null );
  106.          // Hook this object to the dialog.
  107.          this.mDialog.dialog = this;
  108.     },
  109.  
  110.     // promptForSaveToFile:  Display file picker dialog and return selected file.
  111.     promptForSaveToFile: function(aContext, aDefaultFile, aSuggestedFileExtension) {
  112.         var result = "";
  113.  
  114.         // Use file picker to show dialog.
  115.         var nsIFilePicker = Components.interfaces.nsIFilePicker;
  116.         var picker = Components.classes[ "@mozilla.org/filepicker;1" ]
  117.                        .createInstance( nsIFilePicker );
  118.         var bundle = Components.classes[ "@mozilla.org/intl/stringbundle;1" ]
  119.                        .getService( Components.interfaces.nsIStringBundleService )
  120.                            .createBundle( "chrome://global/locale/nsHelperAppDlg.properties");
  121.  
  122.         var windowTitle = bundle.GetStringFromName( "saveDialogTitle" );
  123.         
  124.         var parent = aContext
  125.                         .QueryInterface( Components.interfaces.nsIInterfaceRequestor )
  126.                         .getInterface( Components.interfaces.nsIDOMWindowInternal );
  127.         picker.init( parent, windowTitle, nsIFilePicker.modeSave );
  128.         picker.defaultString = aDefaultFile;
  129.         if (aSuggestedFileExtension) {
  130.             // aSuggestedFileExtension includes the period, so strip it
  131.             picker.defaultExtension = aSuggestedFileExtension.substring(1);
  132.         } else {
  133.             try {
  134.                 picker.defaultExtension = this.mLauncher.MIMEInfo.primaryExtension;
  135.             } catch (ex) {
  136.             }
  137.         }
  138.  
  139.         var wildCardExtension = "*";
  140.         if ( aSuggestedFileExtension ) {
  141.             wildCardExtension += aSuggestedFileExtension;
  142.             picker.appendFilter( wildCardExtension, wildCardExtension );
  143.         }
  144.  
  145.         picker.appendFilters( nsIFilePicker.filterAll );
  146.  
  147.         // Pull in the user's preferences and get the default download directory.
  148.         var prefs = Components.classes[ "@mozilla.org/preferences-service;1" ]
  149.                               .getService( Components.interfaces.nsIPrefBranch );
  150.         try {
  151.             var startDir = prefs.getComplexValue("browser.download.dir",
  152.                                                  Components.interfaces.nsILocalFile);
  153.             if ( startDir.exists() ) {
  154.                 picker.displayDirectory = startDir;
  155.             }
  156.         } catch( exception ) {
  157.         }
  158.  
  159.         var dlgResult = picker.show();
  160.  
  161.         if ( dlgResult == nsIFilePicker.returnCancel ) {
  162.             // Null result means user cancelled.
  163.             return null;
  164.         }
  165.  
  166.  
  167.         // be sure to save the directory the user chose as the new browser.download.dir
  168.         result = picker.file;
  169.  
  170.         if ( result ) {
  171.             var newDir = result.parent;
  172.             prefs.setComplexValue("browser.download.dir",
  173.                                   Components.interfaces.nsILocalFile, newDir);
  174.         }
  175.         return result;
  176.     },
  177.     
  178.     // showProgressDialog:  For now, use old dialog.  At some point, the caller should be
  179.     //                      converted to use the new generic progress dialog (when it's
  180.     //                      finished).
  181.     showProgressDialog: function(aLauncher, aContext) {
  182.          // Display the dialog using the Window Watcher interface.
  183.          var ww = Components.classes["@mozilla.org/embedcomp/window-watcher;1"]
  184.                     .getService( Components.interfaces.nsIWindowWatcher );
  185.          ww.openWindow( null, // no parent
  186.                         "chrome://global/content/nsProgressDlg.xul",
  187.                         null,
  188.                         "chrome,titlebar,minimizable,dialog=yes",
  189.                         aLauncher );
  190.     },
  191.     
  192.     // ---------- implementation methods ----------
  193.  
  194.     // initDialog:  Fill various dialog fields with initial content.
  195.     initDialog : function() {
  196.          // Check if file is executable (in which case, we will go straight to
  197.          // "save to disk").
  198.          var ignore1 = new Object;
  199.          var ignore2 = new Object;
  200.          var tmpFile = this.mLauncher.getDownloadInfo( ignore1, ignore2 );
  201.          if ( tmpFile.isExecutable() ) {
  202.              this.mLauncher.saveToDisk( null, false );
  203.              this.mDialog.close();
  204.              return;
  205.          }
  206.  
  207.          // Put product brand short name in prompt.
  208.          var prompt = this.dialogElement( "prompt" );
  209.          var modified = this.replaceInsert( prompt.firstChild.nodeValue, 1, this.getString( "brandShortName" ) );
  210.          prompt.firstChild.nodeValue = modified;
  211.  
  212.          // Put file name in window title.
  213.          var win   = this.dialogElement( "nsHelperAppDlg" );
  214.          var suggestedFileName = this.mLauncher.suggestedFileName;
  215.  
  216.          var url   = this.mLauncher.source.QueryInterface( Components.interfaces.nsIURL );
  217.          var fname = "";
  218.          this.mSourcePath = url.prePath;
  219.          if ( url ) {
  220.              // A url, use file name from it.
  221.              fname = url.fileName;
  222.              this.mSourcePath += url.directory;
  223.          } else {
  224.              // A generic uri, use path.
  225.              fname = this.mLauncher.source.path;
  226.              this.mSourcePath += url.path;
  227.          }
  228.  
  229.          if (suggestedFileName)
  230.            fname = suggestedFileName;
  231.            
  232.  
  233.          var title = this.replaceInsert( win.getAttribute( "title" ), 1, fname);
  234.          win.setAttribute( "title", title );
  235.  
  236.          // Put content type and location into intro.
  237.          this.initIntro(url);
  238.  
  239.          var iconString = "moz-icon://" + fname + "?size=32&contentType=" + this.mLauncher.MIMEInfo.MIMEType;
  240.  
  241.          this.dialogElement("contentTypeImage").setAttribute("src", iconString);
  242.  
  243.          this.initAppAndSaveToDiskValues();
  244.  
  245.          // always make sure the window starts off with this checked....
  246.          this.dialogElement( "alwaysAskMe" ).checked = true;
  247.  
  248.          // Add special debug hook.
  249.          if ( this.debug ) {
  250.              prompt.setAttribute( "onclick", "dialog.doDebug()" );
  251.          }
  252.  
  253.          // Set up dialog button callbacks.
  254.          var object = this; // "this.onOK()" doesn't work!
  255.          this.mDialog.doSetOKCancel( function () { return object.onOK(); },
  256.                                      function () { return object.onCancel(); } );
  257.  
  258.          // Position it.
  259.          if ( this.mDialog.opener ) {
  260.              this.mDialog.moveToAlertPosition();
  261.          } else {
  262.              this.mDialog.sizeToContent();
  263.              this.mDialog.centerWindowOnScreen();
  264.          }
  265.  
  266.          // Set initial focus
  267.          this.dialogElement( "mode" ).focus();
  268.     },
  269.  
  270.     // initIntro:
  271.     initIntro: function(url) {
  272.         var intro = this.dialogElement( "intro" );
  273.         var desc = this.mLauncher.MIMEInfo.Description;
  274.         var modified;
  275.         if ( desc != "" ) 
  276.         {
  277.           // Use intro with descriptive text.
  278.           modified = this.replaceInsert( this.getString( "intro.withDesc" ), 1, this.mLauncher.MIMEInfo.Description );
  279.         } 
  280.         else 
  281.         {
  282.           // Use intro without descriptive text.
  283.           modified = this.getString( "intro.noDesc" );
  284.         }
  285.  
  286.         modified = this.replaceInsert( modified, 2, this.mLauncher.MIMEInfo.MIMEType );
  287.  
  288.         // if mSourcePath is a local file, then let's use the pretty path name instead of an ugly
  289.         // url...
  290.         var pathString = this.mSourcePath;
  291.         try 
  292.         {
  293.           var fileURL = url.QueryInterface( Components.interfaces.nsIFileURL);
  294.           // All URLs will QI to nsIFileURL.  Need to check the scheme to determine type
  295.           if (fileURL.schemeIs("file"))
  296.           {
  297.              var fileObject = fileURL.file;
  298.              if (fileObject)
  299.              {
  300.                var parentObject = fileObject.parent;
  301.                if (parentObject)
  302.                {
  303.                  pathString = parentObject.unicodePath;
  304.                }
  305.              }
  306.           }
  307.         } catch(ex) {}
  308.  
  309.  
  310.         intro.firstChild.nodeValue = "";
  311.         intro.firstChild.nodeValue = modified;
  312.  
  313.         // Set the location text, which is separate from the intro text so it can be cropped
  314.         var location = this.dialogElement( "location" );
  315.         location.value = pathString;
  316.     },
  317.  
  318.     // initAppAndSaveToDiskValues:
  319.     initAppAndSaveToDiskValues: function() {
  320.  
  321.         // Pre-select the choice the user made last time.
  322.         this.chosenApp = this.mLauncher.MIMEInfo.preferredApplicationHandler;
  323.         var applicationDescription = this.mLauncher.MIMEInfo.applicationDescription;
  324.  
  325.         if (applicationDescription != "")
  326.         {
  327.           this.updateApplicationName(applicationDescription); 
  328.           this.givenDefaultApp = true;
  329.         }
  330.         else if (this.chosenApp && this.chosenApp.unicodePath)
  331.         {
  332.           // If a user-chosen application, show its path.
  333.           this.updateApplicationName(this.chosenApp.unicodePath);
  334.           this.choseApp = true;
  335.         }
  336.         else
  337.          this.updateApplicationName(this.getString("noApplicationSpecified"));
  338.  
  339.         if ( (applicationDescription || this.choseApp) && this.mLauncher.MIMEInfo.preferredAction != this.nsIMIMEInfo.saveToDisk ) 
  340.         {
  341.           var openUsing = this.dialogElement( "openUsing" );
  342.           openUsing.radioGroup.selectedItem = openUsing;
  343.         }
  344.         else 
  345.         {
  346.           // Save to disk.
  347.           var saveToDisk = this.dialogElement( "saveToDisk" );
  348.           saveToDisk.radioGroup.selectedItem = saveToDisk;
  349.           // Disable choose app button.
  350.           this.dialogElement( "chooseApp" ).setAttribute( "disabled", "true" );
  351.         }
  352.     },
  353.  
  354.     updateApplicationName: function(newValue)
  355.     {
  356.       var applicationText = this.getString( "openUsingString" );
  357.       applicationText = this.replaceInsert( applicationText, 1, newValue );
  358.       var expl = this.dialogElement( "openUsing" );
  359.       expl.label = applicationText;
  360.     },
  361.  
  362.     // Enable pick app button if the user chooses that option.
  363.     toggleChoice : function () {
  364.         // See what option is selected.
  365.         if ( this.dialogElement( "openUsing" ).selected ) {
  366.             // We can enable the pick app button.
  367.             this.dialogElement( "chooseApp" ).removeAttribute( "disabled" );
  368.         } else {
  369.             // We can disable the pick app button.
  370.             this.dialogElement( "chooseApp" ).setAttribute( "disabled", "true" );
  371.         }
  372.  
  373.        this.updateOKButton();
  374.     },
  375.  
  376.     processAlwaysAskState : function () 
  377.     {
  378.       // if the user deselected the always ask checkbox, then store that on the mime object for future use...
  379.       if (!this.dialogElement( "alwaysAskMe" ).checked)
  380.       {
  381.         // we first need to rest the user action if the user selected save to disk instead of open...
  382.         // reset the preferred action in this case...we need to do this b4 setting the always ask before handling state
  383.  
  384.         if (!this.dialogElement( "openUsing" ).selected)
  385.         this.mLauncher.MIMEInfo.preferredAction = this.nsIMIMEInfo.saveToDisk;
  386.          
  387.  
  388.         this.mLauncher.MIMEInfo.alwaysAskBeforeHandling = false;
  389.       }
  390.     },
  391.     updateOKButton: function() {
  392.         var ok = false;
  393.         if ( this.dialogElement( "saveToDisk" ).selected ) 
  394.         {
  395.             // This is always OK.
  396.             ok = true;
  397.         } 
  398.         else 
  399.         {
  400.           // only enable the OK button if we have a default app to use or if 
  401.           // the user chose an app....
  402.           if ((this.choseApp && this.chosenApp.unicodePath) || this.givenDefaultApp)
  403.             ok = true;
  404.         }
  405.         
  406.         // Enable Ok button if ok to press.
  407.         this.dialogElement( "ok" ).disabled = !ok;
  408.     },
  409.  
  410.     // onOK:
  411.     onOK: function() {
  412.  
  413.       this.processAlwaysAskState(); 
  414.  
  415.       if ( this.dialogElement( "openUsing" ).selected ) 
  416.       {
  417.          // If no app "chosen" then convert input string to file.
  418.          if (this.chosenApp)
  419.            this.mLauncher.launchWithApplication( this.chosenApp, false );
  420.           else 
  421.            this.mLauncher.launchWithApplication( null, false );
  422.       }
  423.       else
  424.         this.mLauncher.saveToDisk( null, false );
  425.         
  426.       // Unhook dialog from this object.
  427.       this.mDialog.dialog = null;
  428.  
  429.       // Close up dialog by returning true.
  430.       return true;
  431.      //this.mDialog.close();
  432.     },
  433.  
  434.     // onCancel:
  435.     onCancel: function() {
  436.         // Cancel app launcher.
  437.         try {
  438.             this.mLauncher.Cancel();
  439.         } catch( exception ) {
  440.         }
  441.         
  442.         // Unhook dialog from this object.
  443.         this.mDialog.dialog = null;
  444.  
  445.         // Close up dialog by returning true.
  446.         return true;
  447.     },
  448.  
  449.     // dialogElement:  Try cache; obtain from document if not there.
  450.     dialogElement: function( id ) {
  451.          // Check if we've already fetched it.
  452.          if ( !( id in this.elements ) ) {
  453.              // No, then get it from dialog.
  454.              this.elements[ id ] = this.mDialog.document.getElementById( id );
  455.          }
  456.          return this.elements[ id ];
  457.     },
  458.  
  459.     // chooseApp:  Open file picker and prompt user for application.
  460.     chooseApp: function() {
  461.         var nsIFilePicker = Components.interfaces.nsIFilePicker;
  462.         var fp = Components.classes["@mozilla.org/filepicker;1"].createInstance( nsIFilePicker );
  463.         fp.init( this.mDialog,
  464.                  this.getString( "chooseAppFilePickerTitle" ),
  465.                  nsIFilePicker.modeOpen );
  466.  
  467.         // XXX - We want to say nsIFilePicker.filterExecutable or something
  468.         fp.appendFilters( nsIFilePicker.filterAll );
  469.         
  470.         if ( fp.show() == nsIFilePicker.returnOK && fp.file ) {
  471.             // Remember the file they chose to run.
  472.             this.choseApp = true;
  473.             this.chosenApp    = fp.file;
  474.             // Update dialog.
  475.  
  476.             this.updateApplicationName(this.chosenApp.unicodePath);
  477.         }
  478.     },
  479.  
  480.     // setDefault:  Open "edit MIMEInfo" dialog (borrowed from prefs).
  481.     setDefault: function() {
  482.         // Get RDF service.
  483.         var rdf = Components.classes[ "@mozilla.org/rdf/rdf-service;1" ]
  484.                     .getService( Components.interfaces.nsIRDFService );
  485.         // Now ask if it knows about this mime type.
  486.         var exists = false;
  487.         var fileLocator = Components.classes[ "@mozilla.org/file/directory_service;1" ]
  488.                             .getService( Components.interfaces.nsIProperties );
  489.         var file        = fileLocator.get( "UMimTyp", Components.interfaces.nsIFile );
  490.         
  491.         // Get the data source; load it synchronously if it must be
  492.         // initialized.
  493.         var ioService = Components.classes["@mozilla.org/network/io-service;1"].getService(Components.interfaces.nsIIOService);
  494.         var fileurl = ioService.getURLSpecFromFile(file);
  495.         
  496.         var ds = rdf.GetDataSourceBlocking( fileurl );
  497.  
  498.         // Now check if this mimetype is really in there;
  499.         // This is done by seeing if there's a "value" arc from the mimetype resource
  500.         // to the mimetype literal string.
  501.         var mimeRes       = rdf.GetResource( "urn:mimetype:" + this.mLauncher.MIMEInfo.MIMEType );
  502.         var valueProperty = rdf.GetResource( "http://home.netscape.com/NC-rdf#value" );
  503.         var mimeLiteral   = rdf.GetLiteral( this.mLauncher.MIMEInfo.MIMEType );
  504.         exists =  ds.HasAssertion( mimeRes, valueProperty, mimeLiteral, true );
  505.  
  506.         var dlgUrl;
  507.         if ( exists ) {
  508.             // Open "edit mime type" dialog.
  509.             dlgUrl = "chrome://communicator/content/pref/pref-applications-edit.xul";
  510.         } else {
  511.             // Open "add mime type" dialog.
  512.             dlgUrl = "chrome://communicator/content/pref/pref-applications-new.xul";
  513.         }
  514.  
  515.         // Open whichever dialog is appropriate, passing this dialog object as argument.
  516.         this.updateSelf = false; // dialog will reset to true onOK
  517.         this.mDialog.openDialog( dlgUrl,
  518.                                  "_blank",
  519.                                  "chrome,modal=yes,resizable=no",
  520.                                  this );
  521.  
  522.         if (this.updateSelf) {
  523.             // Refresh dialog with updated info about the default action.
  524.             this.initIntro();
  525.             this.initAppAndSaveToDiskValues();
  526.         }
  527.     },
  528.  
  529.     // updateMIMEInfo:  This is called from the pref-applications-edit dialog when the user
  530.     //                  presses OK.  Take the updated MIMEInfo and have the helper app service
  531.     //                  "write" it back out to the RDF datasource.
  532.     updateMIMEInfo: function() {
  533.         this.dumpObjectProperties( "\tMIMEInfo", this.mLauncher.MIMEInfo );
  534.     },
  535.  
  536.     // dumpInfo:
  537.     doDebug: function() {
  538.         const nsIProgressDialog = Components.interfaces.nsIProgressDialog;
  539.         // Open new progress dialog.
  540.         var progress = Components.classes[ "@mozilla.org/progressdialog;1" ]
  541.                          .createInstance( nsIProgressDialog );
  542.         // Show it.
  543.         progress.open( this.mDialog );
  544.     },
  545.  
  546.     // dumpObj:
  547.     dumpObj: function( spec ) {
  548.          var val = "<undefined>";
  549.          try {
  550.              val = eval( "this."+spec ).toString();
  551.          } catch( exception ) {
  552.          }
  553.          this.dump( spec + "=" + val + "\n" );
  554.     },
  555.  
  556.     // dumpObjectProperties
  557.     dumpObjectProperties: function( desc, obj ) {
  558.          for( prop in obj ) {
  559.              this.dump( desc + "." + prop + "=" );
  560.              var val = "<undefined>";
  561.              try {
  562.                  val = obj[ prop ];
  563.              } catch ( exception ) {
  564.              }
  565.              this.dump( val + "\n" );
  566.          }
  567.     },
  568.  
  569.     // getString: Fetch data string from dialog content (and cache it).
  570.     getString: function( id ) {
  571.         // Check if we've fetched this string already.
  572.         if ( !( id in this.strings ) ) {
  573.             // Try to get it.
  574.             var elem = this.mDialog.document.getElementById( id );
  575.             if ( elem
  576.                  &&
  577.                  elem.firstChild
  578.                  &&
  579.                  elem.firstChild.nodeValue ) {
  580.                 this.strings[ id ] = elem.firstChild.nodeValue;
  581.             } else {
  582.                 // If unable to fetch string, use an empty string.
  583.                 this.strings[ id ] = "";
  584.             }
  585.         }
  586.         return this.strings[ id ];
  587.     },
  588.  
  589.     // replaceInsert: Replace given insert with replacement text and return the result.
  590.     replaceInsert: function( text, insertNo, replacementText ) {
  591.         var result = text;
  592.         var regExp = new RegExp("#"+insertNo);
  593.         result = result.replace( regExp, replacementText );
  594.         return result;
  595.     }
  596. }
  597.  
  598. // This Component's module implementation.  All the code below is used to get this
  599. // component registered and accessible via XPCOM.
  600. var module = {
  601.     firstTime: true,
  602.  
  603.     // registerSelf: Register this component.
  604.     registerSelf: function (compMgr, fileSpec, location, type) {
  605.         if (this.firstTime) {
  606.             this.firstTime = false;
  607.             throw Components.results.NS_ERROR_FACTORY_REGISTER_AGAIN;
  608.         }
  609.         compMgr = compMgr.QueryInterface(Components.interfaces.nsIComponentRegistrar);
  610.  
  611.         compMgr.registerFactoryLocation( this.cid,
  612.                                          "Mozilla Helper App Launcher Dialog",
  613.                                          this.contractId,
  614.                                          fileSpec,
  615.                                          location,
  616.                                          type );
  617.     },
  618.  
  619.     // getClassObject: Return this component's factory object.
  620.     getClassObject: function (compMgr, cid, iid) {
  621.         if (!cid.equals(this.cid)) {
  622.             throw Components.results.NS_ERROR_NO_INTERFACE;
  623.         }
  624.  
  625.         if (!iid.equals(Components.interfaces.nsIFactory)) {
  626.             throw Components.results.NS_ERROR_NOT_IMPLEMENTED;
  627.         }
  628.  
  629.         return this.factory;
  630.     },
  631.  
  632.     /* CID for this class */
  633.     cid: Components.ID("{F68578EB-6EC2-4169-AE19-8C6243F0ABE1}"),
  634.  
  635.     /* Contract ID for this class */
  636.     contractId: "@mozilla.org/helperapplauncherdialog;1",
  637.  
  638.     /* factory object */
  639.     factory: {
  640.         // createInstance: Return a new nsProgressDialog object.
  641.         createInstance: function (outer, iid) {
  642.             if (outer != null)
  643.                 throw Components.results.NS_ERROR_NO_AGGREGATION;
  644.  
  645.             return (new nsHelperAppDialog()).QueryInterface(iid);
  646.         }
  647.     },
  648.  
  649.     // canUnload: n/a (returns true)
  650.     canUnload: function(compMgr) {
  651.         return true;
  652.     }
  653. };
  654.  
  655. // NSGetModule: Return the nsIModule object.
  656. function NSGetModule(compMgr, fileSpec) {
  657.     return module;
  658. }
  659.